﻿using GylCSharpUtils.Common.Base.SpecialConverter;
using GylCSharpUtils.Common.Utils;
using GylCSharpUtils.GSerializer.Abstract;
using GylCSharpUtils.GSerializer.SpecialConverter;
using GylCSharpUtils.GSerializer.Template;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace GylCSharpUtils.GSerializer.Template {
    public class GSTManager {

        

        #region 缓存用字典
        private static readonly Dictionary<string, GSTCacheInfo> FullNameDict = new Dictionary<string, GSTCacheInfo>();
        private static readonly Dictionary<ulong, GSTCacheInfo> GSTIDDict = new Dictionary<ulong, GSTCacheInfo>();
        #endregion

        private static GSTManager instance = new GSTManager();

        private GSTManager() {
            LoadSpecialSerializers();
        }
        /// <summary>
        /// 加载专有转换器
        /// </summary>
        private void LoadSpecialSerializers() {
            ASpecialConverter strSer = new StringConverter();
            foreach(var converter in SpecialConverterManager.GetAllSpecialConverter()) {
                // 获取专有类型的全限定名称
                string fullName = AGSerializer.GetTypeName(converter.SpecialType);
                // 获取专有类型的缓存信息
                if(FullNameDict.TryGetValue(fullName, out GSTCacheInfo info)) {
                    // 若有则直接配置转换器
                    info.converter = converter;
                } else {
                    // 若无，手动创建配置转换器
                    var gstid = CalcGSTID(fullName);
                    GSTCacheInfo cache = new GSTCacheInfo(converter.SpecialType, fullName, gstid, null, converter);
                    FullNameDict.Add(fullName, cache);
                    GSTIDDict.Add(gstid, cache);
                }
                
                
                
            }

        }


        /// <summary>
        /// 根据类型获取模板ID，若无模板，则返回0
        /// </summary>
        /// <param name="type">类型</param>
        /// <returns></returns>
        public static ulong GetGSTID(Type type) {
            var cache = GetCacheInfo(type);
            if(cache == null) return 0;
            return cache.GSTID;
        }
        public static ASpecialConverter GetConverter(Type type) {
            var cache = GetCacheInfo(type);
            if(cache == null) return null;
            return cache.converter;
        }
        public static ASpecialConverter GetConverter<T>() {
            return GetConverter(typeof(T));
        }
        public static ASpecialConverter GetConverter(object obj) {
            if(obj == null) return null;
            return GetConverter(obj.GetType());
        }

        public static GSTemplate GetGSTemplate(Type type) {
            var cache = GetCacheInfo(type);
            if(cache== null) return null;
            return cache.template;
        }
        public static GSTemplate GetGSTemplate<T>() {
            return GetGSTemplate(typeof(T));
        }
        public static GSTemplate GetGSTemplate(object obj) {
            if(obj == null) return null;
            return GetGSTemplate(obj.GetType());
        }
        public static GSTemplate GetTemplateByID(ulong gstid) {
            if(GSTIDDict.TryGetValue(gstid, out GSTCacheInfo value)) {
                return value.template;
            }
            return null;
        }



        public static GSTCacheInfo GetCacheInfo<T>() {

            return GetCacheInfo(typeof(T));
        }
        public static GSTCacheInfo GetCacheInfo(object obj) {
            if(obj == null) return null;
            return GetCacheInfo(obj.GetType());

        }


        public static GSTCacheInfo GetCacheInfo(Type type) {
            if(type == null) return null;
            string fullName = AGSerializer.GetTypeName(type);
            if(FullNameDict.TryGetValue(fullName, out GSTCacheInfo cache)) { return cache; }
            PackageFields(type, out List<string> nl, out List<GSTFieldInfo> fieldInfos);
            // 构建长字符串
            ToTemplateStr(type, nl, fieldInfos, out string templateStr, out string pre, out string post);
            // 根据长字符串计算GSTID
            ulong gstid = CalcGSTID(templateStr);
            // 创建模板对象
            var template = new GSTemplate(gstid, type, nl, fieldInfos, templateStr, pre, post);
            // 将模板对象封装
            cache = new GSTCacheInfo(type, fullName, gstid, template);
            // 存放缓存对象
            FullNameDict.Add(fullName, cache);
            GSTIDDict.Add(gstid, cache);

            return cache;
        }

        /// <summary>
        /// 封装字段信息
        /// </summary>
        /// <param name="type">要封装的类</param>
        /// <param name="fieldTypeNameList">字段类型全名列表（不重复）</param>
        /// <param name="fieldInfos">字段封装信息</param>
        private static void PackageFields(Type type, out  List<string> fieldTypeNameList,out List<GSTFieldInfo> fieldInfos) {
            // 找到所有的字段和属性
            var props = type.GetProperties(AGSerializer.GSFieldBindFlag);
            var fields = type.GetFields(AGSerializer.GSFieldBindFlag);
            // 将所有属性和字段的类名去重，并按字符串排序

            var hashSet = new HashSet<string>();
            foreach(var v in props) hashSet.Add(AGSerializer.GetTypeName(v.PropertyType));
            foreach(var v in fields) hashSet.Add(AGSerializer.GetTypeName(v.FieldType));
            fieldTypeNameList = hashSet.ToList();
            fieldTypeNameList.Sort((a, b) => a.CompareTo(b));
            // 将顺序放入dict缓存
            var dict = new Dictionary<string, ushort>();
            for(ushort i = 0; i < fieldTypeNameList.Count; i++) { dict.Add(fieldTypeNameList[i], i); }
            // 将所有的属性和字段封装
            fieldInfos = new List<GSTFieldInfo>();
            fieldInfos.AddRange(props.Select(prop => new GSTFieldInfo(dict[AGSerializer.GetTypeName(prop.PropertyType)], prop)));
            fieldInfos.AddRange(fields.Select(field => new GSTFieldInfo(dict[AGSerializer.GetTypeName(field.FieldType)], field)));
            // 将字段按名称排序
            fieldInfos.Sort((a, b) => a.FieldTypeFullName.CompareTo(b.FieldTypeFullName));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <param name="templateStr"></param>
        /// <param name="typeListStrPre"></param>
        /// <param name="typeListStrPost"></param>
        private static void ToTemplateStr(Type type, List<string> fieldTypeNames, List<GSTFieldInfo> fieldInfos,  out string templateStr, out string typeListStrPre,out string typeListStrPost) {
            // 构建长字符串
            char c1 = ',', c2 = '\"';
            var typeListStrb = new StringBuilder();
            var iTypeName = AGSerializer.GetTypeName(type);
            var fieldTypeIndexListStrb = new StringBuilder();
            var valueTypeFlagListStrb = new StringBuilder();
            var fieldNameListStrb = new StringBuilder();

            for(int i = 0; i < fieldTypeNames.Count; i++) {
                if(i != 0) { typeListStrb.Append(c1); }
                var fullName = fieldTypeNames[i];
                typeListStrb.AppendFormat("{0}{1}{0}", c2, fullName.Equals(typeListStrb) ? "" : fullName);    // 如果与自身类相同，则输出为空字符串
            }
            // 将封装的信息添加到可变字符串中
            for(int i = 0; i < fieldInfos.Count; i++) {
                var fieldInfo = fieldInfos[i];
                if(i != 0) {
                    fieldTypeIndexListStrb.Append(c1);
                    valueTypeFlagListStrb.Append(c1);
                    fieldNameListStrb.Append(c1);
                }
                fieldTypeIndexListStrb.Append(fieldInfo.TemplateIndex);
                valueTypeFlagListStrb.Append(fieldInfo.IsValueType ? 1 : 0);
                fieldNameListStrb.AppendFormat("{0}{1}{0}", c2, fieldInfo.Name);

            }
            // 将所有的信息以Json形式封装
            
            typeListStrPre = string.Format("{0}Type{0}:{0}{1}{0},{0}FieldNum{0}:{2},", c2, iTypeName,fieldInfos.Count);
            string typeListStr = string.Format("{0}FieldTypeList{0}:[{1}],", c2, typeListStrb);
            typeListStrPost = string.Format("{0}FieldName{0}:[{1}],{0}FieldTypeIndex{0}:[{2}],{0}ValueType{0}:[{3}]",
                c2,fieldNameListStrb, fieldTypeIndexListStrb, valueTypeFlagListStrb);
            templateStr = new StringBuilder(typeListStrPre.Length + typeListStr.Length + typeListStrPost.Length + 10)
                .Append("{")
                .Append(typeListStrPre)
                .Append(typeListStr)
                .Append(typeListStrPost)
                .Append("}")
                .ToString();

        }
        
        public static ulong CalcGSTID(string templateStr) {
            if(templateStr == null || string.IsNullOrEmpty(templateStr)) return 0;
            return HashUtils.ComputeHash64(templateStr);

        }


    }
}
